{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot Graphs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def draw_graph(G, pos_nodes, node_names={}, node_size=50, plot_weight=False):\n",
    "    nx.draw(G, pos_nodes, with_labels=False, node_size=node_size, edge_color='gray', arrowsize=30)\n",
    "    \n",
    "    pos_attrs = {}\n",
    "    for node, coords in pos_nodes.items():\n",
    "        pos_attrs[node] = (coords[0], coords[1] + 0.08)\n",
    "        \n",
    "    nx.draw_networkx_labels(G, pos_attrs, font_family='serif', font_size=20)\n",
    "    \n",
    "    \n",
    "    if plot_weight:\n",
    "        pos_attrs = {}\n",
    "        for node, coords in pos_nodes.items():\n",
    "            pos_attrs[node] = (coords[0], coords[1] + 0.08)\n",
    "        \n",
    "        nx.draw_networkx_labels(G, pos_attrs, font_family='serif', font_size=20)\n",
    "        edge_labels=dict([((a,b,),d[\"weight\"]) for a,b,d in G.edges(data=True)])\n",
    "        nx.draw_networkx_edge_labels(G, pos_nodes, edge_labels=edge_labels)\n",
    "    \n",
    "    plt.axis('off')\n",
    "    axis = plt.gca()\n",
    "    axis.set_xlim([1.2*x for x in axis.get_xlim()])\n",
    "    axis.set_ylim([1.2*y for y in axis.get_ylim()])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Undirected Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import networkx as nx\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "G = nx.Graph()\n",
    "V = {'Dublin', 'Paris', 'Milan', 'Rome'}\n",
    "E = [('Milan','Dublin'), ('Milan','Paris'), ('Paris','Dublin'), ('Milan','Rome')]\n",
    "G.add_nodes_from(V)\n",
    "G.add_edges_from(E)\n",
    "draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"V = {G.nodes}\")\n",
    "print(f\"E = {G.edges}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "{G.degree(v): v for v in G.nodes}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "print(f\"Graph Order: {G.number_of_nodes()}\")\n",
    "print(f\"Graph Size: {G.number_of_edges()}\")\n",
    "print(f\"Degree for nodes: { {v: G.degree(v) for v in G.nodes} }\")\n",
    "print(f\"Neighbors for nodes: { {v: list(G.neighbors(v)) for v in G.nodes} }\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ego_graph_milan = nx.ego_graph(G, \"Milan\")\n",
    "print(f\"Nodes: {ego_graph_milan.nodes}\")\n",
    "print(f\"Edges: {ego_graph_milan.edges}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_nodes = {'London', 'Madrid'}\n",
    "new_edges = [('London','Rome'), ('Madrid','Paris')]\n",
    "G.add_nodes_from(new_nodes)\n",
    "G.add_edges_from(new_edges)\n",
    "print(f\"V = {G.nodes}\")\n",
    "print(f\"E = {G.edges}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "node_remove = {'London', 'Madrid'}\n",
    "G.remove_nodes_from(node_remove)\n",
    "print(f\"V = {G.nodes}\")\n",
    "print(f\"E = {G.edges}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "node_edges = [('Milan','Dublin'), ('Milan','Paris')]\n",
    "G.remove_edges_from(node_edges)\n",
    "print(f\"V = {G.nodes}\")\n",
    "print(f\"E = {G.edges}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(nx.to_edgelist(G))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(nx.to_pandas_adjacency(G))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Directed Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import networkx as nx\n",
    "G = nx.DiGraph()\n",
    "V = {'Dublin', 'Paris', 'Milan', 'Rome'}\n",
    "E = [('Milan','Dublin'), ('Paris','Milan'), ('Paris','Dublin'), ('Milan','Rome')]\n",
    "G.add_nodes_from(V)\n",
    "G.add_edges_from(E)\n",
    "print(nx.to_pandas_edgelist(G))\n",
    "print(nx.to_pandas_adjacency(G))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"Indegree for nodes: { {v: G.in_degree(v) for v in G.nodes} }\")\n",
    "print(f\"Outegree for nodes: { {v: G.out_degree(v) for v in G.nodes} }\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Weighted Directed Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import networkx as nx\n",
    "G = nx.MultiDiGraph()\n",
    "V = {'Paris', 'Dublin','Milan', 'Rome'}\n",
    "E = [ ('Paris','Dublin', 11), ('Paris','Milan', 8),\n",
    "     ('Milan','Rome', 5),('Milan','Dublin', 19)]\n",
    "G.add_nodes_from(V)\n",
    "G.add_weighted_edges_from(E)\n",
    "draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500, plot_weight=True)\n",
    "print(nx.to_pandas_edgelist(G))\n",
    "print(nx.to_pandas_adjacency(G))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bipartite Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_nodes = 10\n",
    "n_edges = 12\n",
    "bottom_nodes = [ith for ith in range(n_nodes) if ith % 2 ==0]\n",
    "top_nodes = [ith for ith in range(n_nodes) if ith % 2 ==1]\n",
    "iter_edges = zip(\n",
    "    np.random.choice(bottom_nodes, n_edges),  \n",
    "    np.random.choice(top_nodes, n_edges))\n",
    "edges = pd.DataFrame([\n",
    "    {\"source\": a, \"target\": b} for a, b in iter_edges])\n",
    "B = nx.Graph()\n",
    "B.add_nodes_from(bottom_nodes, bipartite=0)\n",
    "B.add_nodes_from(top_nodes, bipartite=1)\n",
    "B.add_edges_from([tuple(x) for x in edges.values])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from networkx.drawing.layout import bipartite_layout\n",
    "pos = bipartite_layout(B, bottom_nodes)\n",
    "nx.draw_networkx(B, pos=pos)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Multi Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import networkx as nx\n",
    "directed_multi_graph = nx.MultiDiGraph()\n",
    "V = {'Dublin', 'Paris', 'Milan', 'Rome'}\n",
    "E = [('Milan','Dublin'), ('Milan','Dublin'), ('Paris','Milan'), ('Paris','Dublin'), ('Milan','Rome'), ('Milan','Rome')]\n",
    "directed_multi_graph.add_nodes_from(V)\n",
    "directed_multi_graph.add_edges_from(E)\n",
    "\n",
    "draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
